﻿using System;

namespace DimensionsHelper.Common.Metadata.Script;

/// <summary>
///     表示表达式的基础类型。
/// </summary>
public abstract class ExpressionBasicType
{
    /// <summary>
    ///     表示表达式的基础类型。
    /// </summary>
    protected ExpressionBasicType(BasicTypes type)
    {
        Type = type;
    }

    /// <summary>
    ///     基础类型。
    /// </summary>
    public BasicTypes Type { get; }

    /// <summary>
    ///     获取指定转换后的类型的值。
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public abstract object GetValue(BasicTypes type);

    /// <summary>
    ///     判断给定类型可以隐式转换。
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public abstract bool CanImplicitlyConvert(BasicTypes type);

    /// <summary>
    ///     将当前类型转换成其他基础类型值。此值是左值。
    /// </summary>
    /// <returns></returns>
    public abstract ExpressionBasicType ConvertTo(BasicTypes type);

    /// <summary>
    ///     和其他基础类型值进行加法运算。此值是左值。
    /// </summary>
    /// <returns></returns>
    public abstract ExpressionBasicType Add(ExpressionBasicType expression);

    /// <summary>
    ///     和其他基础类型值进行减法运算。此值是左值。
    /// </summary>
    public abstract ExpressionBasicType Subtract(ExpressionBasicType expression);

    /// <summary>
    ///     和其他类型值进行乘法运算。此值是左值。
    /// </summary>
    public abstract ExpressionBasicType Multiply(ExpressionBasicType expression);

    /// <summary>
    ///     和其他类型值进行除法运算。此值是左值。
    /// </summary>
    public abstract ExpressionBasicType Divide(ExpressionBasicType expression);

    /// <summary>
    ///     和其他类型值进行取模运算。此值是左值。
    /// </summary>
    public abstract ExpressionBasicType Modulo(ExpressionBasicType expression);

    /// <summary>
    ///     和其他类型值进行比较。此值是左值。
    /// </summary>
    /// <remarks>
    ///     返回结果
    ///     <list type="bullet">
    ///         <item>
    ///             <term>
    ///                 1
    ///             </term>
    ///             <description>
    ///                 <c>
    ///                     expression <![CDATA[<]]> this
    ///                 </c>
    ///             </description>
    ///         </item>
    ///         <item>
    ///             <term>
    ///                 0
    ///             </term>
    ///             <description>
    ///                 <c>expression == this</c>
    ///             </description>
    ///         </item>
    ///         <item>
    ///             <term>
    ///                 -1
    ///             </term>
    ///             <description>
    ///                 <c>expression > this</c>
    ///             </description>
    ///         </item>
    ///     </list>
    /// </remarks>
    public virtual int Compare(ExpressionBasicType expression)
    {
        if (expression.CanImplicitlyConvert(BasicTypes.Double))
        {
            double dbRes = (double)GetValue(BasicTypes.Double) - (double)expression.GetValue(BasicTypes.Double);
            return dbRes > 0 ? 1 : dbRes == 0 ? 0 : -1;
        }

        int iRes = (int)GetValue(BasicTypes.Integer) - (int)expression.GetValue(BasicTypes.Integer);
        return iRes > 0 ? 1 : iRes == 0 ? 0 : -1;
    }

    /// <summary>
    ///     判断当前类型值是否大于给定类型值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType IsGreaterThan(ExpressionBasicType expression)
    {
        return new ExpressionBoolean(Compare(expression) > 0);
    }

    /// <summary>
    ///     判断当前类型值是否大于或等于给定类型值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType IsGreaterThanEqual(ExpressionBasicType expression)
    {
        return new ExpressionBoolean(Compare(expression) >= 0);
    }

    /// <summary>
    ///     判断当前类型值是否等于给定类型值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType IsEqualTo(ExpressionBasicType expression)
    {
        return new ExpressionBoolean(Compare(expression) == 0);
    }

    /// <summary>
    ///     判断当前类型值是否小于给定类型值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType IsLessThan(ExpressionBasicType expression)
    {
        return new ExpressionBoolean(Compare(expression) < 0);
    }

    /// <summary>
    ///     判断当前类型值是否小于或等于给定类型值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType IsLessThanEqual(ExpressionBasicType expression)
    {
        return new ExpressionBoolean(Compare(expression) <= 0);
    }

    /// <summary>
    ///     判断当前类型值和给定类型值的且逻辑真值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType And(ExpressionBasicType expression)
    {
        return new ExpressionBoolean((bool)GetValue(BasicTypes.Boolean) &&
                                     (bool)expression.GetValue(BasicTypes.Boolean));
    }

    /// <summary>
    ///     判断当前类型值和给定类型值的或逻辑真值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType Or(ExpressionBasicType expression)
    {
        return new ExpressionBoolean((bool)GetValue(BasicTypes.Boolean) ||
                                     (bool)expression.GetValue(BasicTypes.Boolean));
    }

    /// <summary>
    ///     判断当前类型值和给定类型值的异或逻辑真值。
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public virtual ExpressionBasicType Xor(ExpressionBasicType expression)
    {
        return new ExpressionBoolean((bool)GetValue(BasicTypes.Boolean) !=
                                     (bool)expression.GetValue(BasicTypes.Boolean));
    }

    /// <summary>
    ///     计算 + - * / % 五种数学计算的数值，优先使用高精度类型。
    /// </summary>
    /// <param name="right">右值表达式</param>
    /// <param name="calcType">计算类型</param>
    /// <returns></returns>
    protected object Calculate(ExpressionBasicType right, CalculateTypes calcType)
    {
        if (CanImplicitlyConvert(BasicTypes.Double) || right.CanImplicitlyConvert(BasicTypes.Double))
        {
            double dbThis = (double)GetValue(BasicTypes.Double);
            double dbRight = (double)right.GetValue(BasicTypes.Double);

            if ((calcType == CalculateTypes.Divide || calcType == CalculateTypes.Modulo) && dbRight == 0)
            {
                throw ThrowHelper.ZeroCannotBeDivisor();
            }

            switch (calcType)
            {
                case CalculateTypes.Add:
                    return dbThis + dbRight;
                case CalculateTypes.Subtract:
                    return dbThis - dbRight;
                case CalculateTypes.Multiply:
                    return dbThis * dbRight;
                case CalculateTypes.Divide:
                    return dbThis / dbRight;
                case CalculateTypes.Modulo:
                    return dbThis % dbRight;
                default:
                    throw new InvalidOperationException();
            }
        }

        if (CanImplicitlyConvert(BasicTypes.Categorical) || right.CanImplicitlyConvert(BasicTypes.Categorical))
        {
            Categorical catThis = (Categorical)GetValue(BasicTypes.Categorical);
            Categorical catRight = (Categorical)right.GetValue(BasicTypes.Categorical);

            switch (calcType)
            {
                case CalculateTypes.Add:
                    return catThis.Union(catRight);
                case CalculateTypes.Subtract:
                    return catThis.Difference(catRight);
                case CalculateTypes.Multiply:
                    return catThis.Intersection(catRight);
                case CalculateTypes.Divide:
                    return catThis.XUnion(catRight);
                default:
                    throw new InvalidOperationException();
            }
        }

        int intThis = (int)GetValue(BasicTypes.Integer);
        int intRight = (int)right.GetValue(BasicTypes.Integer);

        if ((calcType == CalculateTypes.Divide || calcType == CalculateTypes.Modulo) && intRight == 0)
        {
            throw ThrowHelper.ZeroCannotBeDivisor();
        }

        switch (calcType)
        {
            case CalculateTypes.Add:
                return intThis + intRight;
            case CalculateTypes.Subtract:
                return intThis - intRight;
            case CalculateTypes.Multiply:
                return intThis * intRight;
            case CalculateTypes.Divide:
                return intThis / intRight;
            case CalculateTypes.Modulo:
                return intThis % intRight;
            default:
                throw new InvalidOperationException();
        }
    }
}